﻿using System;
using System.Collections.Generic;
using System.Linq;
using Xenta.Api;
using Xenta.Entities;
using Xenta.Security;
using Xenta.Utils;

namespace Xenta.Operations
{
    #region __General__

    /// <summary>
    /// Creates a language.
    /// </summary>
    public class CreateLanguagePack : CreateAuditable<LanguagePackEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("GLBLZ");
            }
        }

        /// <summary>
        /// Gets or sets the language identifier.
        /// </summary>
        public int LanguageID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the code.
        /// </summary>
        public string Code
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(String.IsNullOrWhiteSpace(Code))
                return Err.Of<LanguagePackEntity>().NotValid("Code");
            return base.Validate(uow);
        }

        protected override void Construct(IUnitOfWork uow, LanguagePackEntity entity)
        {
            base.Construct(uow, entity);

            entity.Language = uow.Execute(new GetLanguage
            {
                ThrowsException = true,
                EntityID = LanguageID
            }).Result;
            entity.Code = Code.TrimToLen(10);
            entity.Content = LanguagePackHelper.Assemble(new Dictionary<String, String>());
        }

        #endregion
    }

    /// <summary>
    /// Updates the language.
    /// </summary>
    public class UpdateLanguagePack : UpdateAuditable<LanguagePackEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("GLBLZ");
            }
        }

        /// <summary>
        /// Gets or sets the language identifier.
        /// </summary>
        public int LanguageID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the code.
        /// </summary>
        public string Code
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(String.IsNullOrWhiteSpace(Code))
                return Err.Of<LanguagePackEntity>().NotValid("Code");
            return base.Validate(uow);
        }

        protected override void Revise(IUnitOfWork uow, LanguagePackEntity entity)
        {
            base.Revise(uow, entity);

            entity.Code = Code.TrimToLen(10);
            entity.Language = uow.Execute(new GetLanguage
            {
                ThrowsException = true,
                EntityID = LanguageID
            }).Result;
        }

        #endregion
    }

    /// <summary>
    /// Deletes the language.
    /// </summary>
    public class DeleteLanguagePack : DeleteEntity<LanguagePackEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("GLBLZ");
            }
        }

        #endregion
    }

    /// <summary>
    /// Gets the language by identifier.
    /// </summary>
    public class GetLanguagePack : GetEntity<LanguagePackEntity>,
        IApiOperation
    {
    }

    /// <summary>
    /// Gets the language by code.
    /// </summary>
    public class GetLanguagePackByCode : QueryEntity<LanguagePackEntity>,
        IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets or sets the language identifier.
        /// </summary>
        public int LanguageID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the language code.
        /// </summary>
        public string Code
        {
            get;
            set;
        }

        #endregion

        #region Options

        protected override bool Cacheable
        {
            get
            {
                return true;
            }
        }

        #endregion

        #region Utilities

        protected override IQueryable<LanguagePackEntity> Build(IUnitOfWork uow, IQueryable<LanguagePackEntity> query)
        {
            string code = StringHelper.EnsureNotNull(Code).TrimToLen(10);
            return base.Build(uow, query).Where(x => 
                x.Code.Equals(code) && 
                x.Language.EntityID == LanguageID);
        }

        #endregion
    }

    #endregion

    #region __Search__

    /// <summary>
    /// Search over language pack collection.
    /// </summary>
    public class SearchLanguagePacks : SearchOperation<LanguagePackEntity>,
        IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets or sets the language identifier.
        /// </summary>
        public int? LanguageID
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<LanguagePackEntity> Build(IUnitOfWork uow, IQueryable<LanguagePackEntity> query)
        {
            query = base.Build(uow, query);
            if(LanguageID.HasValue)
                query = query.Where(x => x.Language.EntityID == LanguageID.Value);
            query = query.OrderBy(x => x.Code);
            return query;
        }

        #endregion
    }

    #endregion

    #region __Content__

    /// <summary>
    /// Gets the language pack content as a dictionary.
    /// </summary>
    public class GetLanguagePackContent : OperateEntityByID<IDictionary<String, String>>,
        IApiOperation
    {
        #region Utilities

        protected override void Work(IUnitOfWork uow, out IDictionary<String, String> result)
        {
            result = null;
            var pack = uow.Execute(new GetLanguagePack
            {
                EntityID = EntityID
            }).Result;
            if(pack == null)
                return;
            result = LanguagePackHelper.Disassemble(pack.Content);
        }

        #endregion
    }

    /// <summary>
    /// Gets the language pack content as a dictionary.
    /// </summary>
    public class GetLanguagePackContentByCode : Operation<IDictionary<String, String>>,
        IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets or sets the language identifier.
        /// </summary>
        public int LanguageID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the code.
        /// </summary>
        public string Code
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out IDictionary<String, String> result)
        {
            result = uow.Execute(new GetLanguagePackContent
            {
                EntityID = uow.Execute(new GetLanguagePackByCode
                {
                    LanguageID = LanguageID,
                    Code = Code
                }).Result.EntityID
            }).Result;
        }

        #endregion
    }

    /// <summary>
    /// Sets the language pack content from a dictionary.
    /// </summary>
    public class SetLanguagePackContent : UpdateAuditable<LanguagePackEntity>
    {
        #region Properties

        /// <summary>
        /// Gets or sets the language pack content.
        /// </summary>
        public IDictionary<String, String> Content
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(Content == null)
                return Err.Of<LanguagePackEntity>().NotValid("Content");
            return base.Validate(uow);
        }

        protected override void Revise(IUnitOfWork uow, LanguagePackEntity entity)
        {
            base.Revise(uow, entity);
            if(Content != null)
                entity.Content = LanguagePackHelper.Assemble(Content);
        }

        #endregion
    }

    /// <summary>
    /// Merges the language pack content from a dictionary.
    /// </summary>
    /// <remarks>
    /// The operation allows to define the merge destination. 
    /// Use 'A' character for the old content and 'B' character 
    /// for the new content. The 'B' is used by default. For example, 
    /// if destination is 'A', the values from 'B' are being used to 
    /// overwrite the matching entries. And vice versa, if destination 
    /// is 'B', the values from 'A' are being used to overwrite the 
    /// matching entries.
    /// </remarks>
    public class MergeLanguagePackContent : OperateEntityByID
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public MergeLanguagePackContent()
        {
            Destination = 'B';
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the language pack content.
        /// </summary>
        public IDictionary<String, String> Content
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the destination. 'A' - old content, 'B' - new content.
        /// </summary>
        public char Destination
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow)
        {
            if(Content == null)
                return;

            var oldContent = uow.Execute(new GetLanguagePackContent
            {
                EntityID = EntityID
            }).Result;
            if(oldContent == null)
                return;

            if(Destination == 'B')
            {
                foreach(var i in oldContent.Where(x => Content.ContainsKey(x.Key)))
                    Content[i.Key] = i.Value;
                uow.Execute(new SetLanguagePackContent
                {
                    EntityID = EntityID,
                    Content = Content
                });
            }
            else
            {
                foreach(var i in Content)
                    oldContent[i.Key] = i.Value;
                uow.Execute(new SetLanguagePackContent
                {
                    EntityID = EntityID,
                    Content = oldContent
                });
            }
        }

        #endregion
    }

    #endregion
}
